/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package technotheist.imgcog.filter;

import technotheist.imgcog.image.ImageTemplate;

/**
 *
 * @author Mitch
 */
public class ConvolveFilter extends Filter {

    public ConvolveFilter(ConvolveFilterRunner runner) {
        super(runner);
    }
    
    protected static abstract class BiconvolveFilterRunner extends ConvolveFilterRunner {
        
        @Constant
        protected final float[] subKernel;
        protected final int subKernelSize;

        public BiconvolveFilterRunner(ImageTemplate img, boolean channelConvolve, 
                int kernelSize, float[] kernel, int subKernelSize, float[] subKernel) {
            super(img, channelConvolve, kernelSize, kernel);
            this.subKernel = subKernel;
            this.subKernelSize = subKernelSize;
        }
        
        @Override
        public abstract void run();
        
        protected float calcSubConvolve(int x, int y, int p) {
            float pSum = 0;
            float cSum = 0;
            //
            int i = x - subKernelSize / 2;
            int j = y - subKernelSize / 2;
            int mi = x + subKernelSize / 2;
            int mj = y + subKernelSize / 2;
            //
            float v = 0;
            float c = 0;
            while (j <= mj) {
                if (j >= 0 && j < imgHeight) {
                    while (i <= mi) {
                        if (i >= 0 && i < imgWidth) {
                            //
                            v = input[imgIndex(i, j, p)];
                            c = subKernel[convIndex(i - x, j - y, p)];
                            pSum += v * c;
                            cSum += c;
                            //
                        }
                        i += 1;
                    }
                }
                j += 1;
            }
            return pSum / cSum;
        }
        
        
        protected int subConvIndex(int dx, int dy, int p) {
            int x = dx + subKernelSize / 2;
            int y = dy + subKernelSize / 2;
            if(channelConvolve) {
                return index3D(x, y, p, subKernelSize, imgPixSize);
            }
            return index2D(x, y, subKernelSize);
        }
    }

    protected static class ConvolveFilterRunner extends FilterRunner {

        @Constant
        protected final float[] kernel;
        protected final int kernelSize;
        protected final boolean channelConvolve;

        protected ConvolveFilterRunner(ImageTemplate img, boolean channelConvolve, int kernelSize, float[] kernel) {
            super(img.width, img.height, img.pixSize);
            this.kernel = kernel;
            this.kernelSize = kernelSize;
            this.channelConvolve = channelConvolve;
        }

        @Override
        public void run() {
            int x = getGlobalId(0);
            int y = getGlobalId(1);
            //
            int p = 0;
            while (p < imgPixSize) {
                output[imgIndex(x, y, p)] = calcConvolve(x, y, p);
                p += 1;
            }
        }

        protected float calcConvolve(int x, int y, int p) {
            float pSum = 0;
            float cSum = 0;
            //
            int i = x - kernelSize / 2;
            int j = y - kernelSize / 2;
            int mi = x + kernelSize / 2;
            int mj = y + kernelSize / 2;
            //
            float v = 0;
            float c = 0;
            while (j <= mj) {
                if (j >= 0 && j < imgHeight) {
                    while (i <= mi) {
                        if (i >= 0 && i < imgWidth) {
                            //
                            v = input[imgIndex(i, j, p)];
                            c = kernel[convIndex(i - x, j - y, p)];
                            pSum += v * c;
                            cSum += c;
                            //
                        }
                        i += 1;
                    }
                }
                j += 1;
            }
            return pSum / cSum;
        }

        protected int convIndex(int dx, int dy, int p) {
            int x = dx + kernelSize / 2;
            int y = dy + kernelSize / 2;
            if(channelConvolve) {
                return index3D(x, y, p, kernelSize, imgPixSize);
            }
            return index2D(x, y, kernelSize);
        }
    }
    
    
}
